package self.security.filter;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.self.service.ITokenService;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import self.enumerate.ResponseCode;
import self.pojo.User;
import self.result.R;
import self.security.entity.SecurityUser;
import self.security.utils.HttpResponseUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 作者: liYu
 * 标题: LoginFilter
 * 项目名字 CD-Java-2307-springboot-parent
 * 目的: 这个是登陆的过滤器
 * 原来的这个登陆的类已经不满足我们的要求了...
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    //这个值只能通过配置的时候 传递过来
    private AuthenticationManager authenticationManager;

    //token的业务类
    private ITokenService tokenService;

    public LoginFilter(AuthenticationManager authenticationManager, ITokenService tokenService) {
        this.authenticationManager = authenticationManager;
        this.tokenService = tokenService;
        this.setPostOnly(true);
        //这里还要设置处理的登陆的URL地址是谁?
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login", HttpMethod.POST.name()));
    }

    /**
     * 这个方法就是尝试登陆的这个方法
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //首先就要去我们的前端传递的数据中拿到这个用户名和密码
        //注意：原来传递的是键值对 但是我们前后分离的时候 传递的是JSON格式的数据
        try {
            User user = getUserInfoByReq(request);
            //接下来我们就可以封装成
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
            //接下来就可以调用这个API完成认证的调用了
            return this.authenticationManager.authenticate(token);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过req拿到前端传递的用户名和密码对象
     *
     * @param request
     * @return
     */
    private User getUserInfoByReq(HttpServletRequest request) throws IOException {
        //提前设置好缓冲区
        byte[] buf = new byte[1024];
        //获取输入流
        ServletInputStream inputStream = request.getInputStream();
        //将流中的数据读取到缓冲区去
        int readLength = inputStream.read(buf);
        //接下来就可以将数据弄成字符串
        String sendStr = new String(buf, 0, readLength);
        //接下来就要将前端传递的JSON格式的字符串弄成 Java对象
        return JSON.parseObject(sendStr, User.class);
    }

    /**
     * 登陆成功之后的回调函数
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //拿出这个username和userId对象  生成我们的这个token对象
        SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
        User user = securityUser.getUser();
        //下面就可以拿到这个用户名
        String username = user.getUserName();
        //就可以拿到这个id
        Long id = user.getId();
        //接下来就要生成token对象
        String token = this.tokenService.createToken(null,username, String.valueOf(id));
        log.info("生成的token是:{}", token);
        //TODO 还要将权限信息和角色信息取出来放到内存中 以便后续的使用
        //接下来就是将数据返回到客户端
        R data = R.ok().data("token", token).data("username", username).data("id", id);
        HttpResponseUtils.sendResponse(response, data);
    }

    /**
     * 登陆失败之后的回调函数
     *
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        R error = R.error(ResponseCode.USERNAME_PASSWORD_EXCEPTION.getCode(),
                ResponseCode.USERNAME_PASSWORD_EXCEPTION.getMessage());
        HttpResponseUtils.sendResponse(response,error);
    }
}
